package dohttp

import (
	"bytes"
	"encoding/json"

	// "fmt"
	"io"
	"io/ioutil"
	"net/http"
	"unsafe"

	// "errors"
	"manygo/mytools"
	"mime/multipart"
	"regexp"
	"strings"
	"time"
)

var (
	//HTTPClient 300
	HTTPClient = &http.Client{
		Timeout: 300 * time.Second,
	}
)

//ResInfo 4234
type ResInfo struct {
	RID              string
	IsRun            bool
	Status           bool
	Msg              string
	URL              string
	Method           string
	ReqData          string
	ReqHeader        map[string]string
	ReqContentLength int64
	StartTime        int64
	EndTime          int64
	EndTimeSecond    int64
	SpeedTime        int64
	ResStatus        string
	StatusCode       int
	ResHeader        map[string]string
	ResContentLength int64
	ResBody          string
	ResParam         map[string]string
}

//ResCheck 23
type ResCheck struct {
	Position string // 响应代码 statuscode	响应头 header 	响应体  body
	Method   string // 提取json json 	提取正则  re	提取全部 all
	Context  string // eq eqn contain	containn match matchn sizeeq sizeeqn sizebigger sizesmaller
	Value    string
	Num      int
}

//ResGet 123
type ResGet struct {
	Position string
	Method   string
	Num      int
	Name     string
}

//Ready zhunb
type Ready struct {
	ID           string
	Method       string
	URL          string
	Data         string
	Headers      map[string]string
	HasParam     bool
	DataForm     string // json form file
	ParamMap     map[string][]string
	ThinkTime    time.Duration
	NextReady    []string
	ContentType  string //application/x-www-form-urlencoded  multipart/form-data application/json
	IsCheck      bool
	Msg          string
	LocalMap     map[string]string
	ResChecks    []ResCheck
	ResGets      []ResGet
	HasResBody   bool
	HasResHeader bool
	IsLook       bool
}

//SampleGet 123
func SampleGet(url string) (string, error) {
	hh := &http.Client{
		Timeout: time.Second,
	}

	res, err := hh.Get(url)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	return string(body), nil

}

//SamplePostJSON 123
func SamplePostJSON(url string, data []byte) (string, error) {
	resp, err := http.Post(url,
		"application/json",
		bytes.NewBuffer(data))
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	return string(body), nil

}

//Check 1231
func (r *Ready) Check() (bool, string) {
	if r.ID == "" {
		r.ID = mytools.MyID()
	}

	reg1 := regexp.MustCompile(`\$\{(.+?)\}`)
	r.ParamMap = make(map[string][]string)
	if reg1 == nil {
		r.Msg = "reg faild"

		return false, "reg faild"
	}
	if r.Method == "" {
		r.Msg = "no method"
		return false, "no method"
	}
	methodList := []string{"get", "post", "put", "delete"}
	isMethod := false
	for _, v := range methodList {
		if strings.ToLower(r.Method) == v {
			isMethod = true
			break
		}

	}
	if !isMethod {
		r.Msg = "error method"
		return false, "error method"
	}
	if strings.ToLower(r.Method) == "post" || strings.ToLower(r.Method) == "put" {
		if res, ok := r.Headers["content-type"]; ok {
			r.ContentType = res

		} else {
			if res, ok = r.Headers["Content-Type"]; ok {
				r.ContentType = res
			} else {
				if r.DataForm != "file" {
					r.Msg = "header no content-type"
					return false, "header no content-type"
				}
			}

		}
		if r.DataForm == "" {
			r.Msg = "no dataForm"
			return false, "no dataForm"
		}
		dataFormList := []string{"json", "file", "form"}
		isdataForm := false
		for _, v := range dataFormList {
			if strings.ToLower(r.DataForm) == v {
				isdataForm = true
				break
			}

		}
		if !isdataForm {
			r.Msg = "error isdataForm"
			return false, "error isdataForm"
		}
	}

	if r.URL == "" {
		r.Msg = "no url"
		return false, "no url"
	} else if !strings.HasPrefix(strings.ToLower(r.URL), "http") {
		r.Msg = "no protocol"

		return false, "no protocol"
	}
	result1 := reg1.FindAllStringSubmatch(r.URL, -1)
	if len(result1) == 0 {
		r.HasParam = false || r.HasParam
	} else {
		r.HasParam = true
		for _, v := range result1 {
			if _, ok := r.ParamMap[v[1]]; !ok {
				r.ParamMap[v[1]] = make([]string, 0, 0)
				r.ParamMap[v[1]] = append(r.ParamMap[v[1]], "tbyUrl")

			}

		}
	}
	result1 = reg1.FindAllStringSubmatch(r.Data, -1)
	if len(result1) == 0 {
		r.HasParam = false || r.HasParam
	} else {
		r.HasParam = true
		for _, v := range result1 {
			if res, ok := r.ParamMap[v[1]]; !ok {
				r.ParamMap[v[1]] = make([]string, 0, 0)
				r.ParamMap[v[1]] = append(r.ParamMap[v[1]], "tbyData")

			} else {
				hasData := false
				for _, v2 := range res {
					if v2 == "tbyData" {
						hasData = true
					}
				}
				if !hasData {
					r.ParamMap[v[1]] = append(r.ParamMap[v[1]], "tbyData")
				}

			}

		}
	}
	for k, v := range r.Headers {
		result1 = reg1.FindAllStringSubmatch(v, -1)
		if len(result1) == 0 {
			r.HasParam = false || r.HasParam
		} else {
			r.HasParam = true
			for _, v := range result1 {
				if res, ok := r.ParamMap[v[1]]; !ok {
					r.ParamMap[v[1]] = make([]string, 0, 0)
					r.ParamMap[v[1]] = append(r.ParamMap[v[1]], k)

				} else {
					hasData := false
					for _, v2 := range res {
						if v2 == k {
							hasData = true
						}
					}
					if !hasData {
						r.ParamMap[v[1]] = append(r.ParamMap[v[1]], k)
					}

				}

			}
		}

	}
	if len(r.ResChecks) != 0 {
		for _, rc := range r.ResChecks {
			if rc.Position == "header" {
				r.HasResHeader = true
			} else if rc.Position == "body" {
				r.HasResBody = true
			} else {
				if rc.Position != "statuscode" {
					r.Msg = "no rescheck position"

					return false, "no rescheck position"

				}
			}
			mm := [3]string{"json", "re", "all"}
			var ismm bool
			for _, mmm := range mm {
				if rc.Method == mmm {
					ismm = true
					break
				}
			}
			if !ismm {
				r.Msg = "no rescheck method"

				return false, "no rescheck method"

			}
			cc := []string{"eq", "eqn", "contain", "containn", "match", "matchn", "sizeeq", "sizeeqn", "sizebigger", "sizesmaller"}
			ismm = false
			for _, mmm := range cc {
				if rc.Context == mmm {
					ismm = true
					break
				}
			}
			if !ismm {
				r.Msg = "no rescheck context"

				return false, "no rescheck context"

			}
			if rc.Num == 0 {
				rc.Num = 1
			}

			// rr:=[3]string{"statuscode","header","body"}
		}
	}
	if len(r.ResGets) != 0 {
		for _, rg := range r.ResGets {
			if rg.Position == "header" {
				r.HasResHeader = true
			} else if rg.Position == "body" {
				r.HasResBody = true
			} else {
				if rg.Position != "statuscode" {
					r.Msg = "no resget position"

					return false, "no resget position"

				}
			}
			mm := [3]string{"json", "re", "all"}
			var ismm bool
			for _, mmm := range mm {
				if rg.Method == mmm {
					ismm = true
					break
				}
			}
			if ismm {
				r.Msg = "no rescheck method"

				return false, "no rescheck method"

			}
			if rg.Num == 0 {
				rg.Num = 1
			}

		}
	}

	r.IsCheck = true
	return r.IsCheck, "ok"
}

//Run 34
func (r *Ready) Run(m map[string]string) (bool, *ResInfo) {
	// resMap := make(map[string]interface{})
	resInfo := new(ResInfo)
	resInfo.RID = r.ID

	resInfo.Method = strings.ToUpper(r.Method)

	if !r.IsCheck {
		resInfo.URL = r.URL
		resInfo.Status = false
		resInfo.Msg = r.Msg
		return false, resInfo
	}

	if len(r.LocalMap) != 0 {
		for k, v := range m {
			m[k] = v
		}

	}
	if r.IsLook {
		resInfo.ResParam = m
	}
	murl := r.URL
	var mdata string = r.Data
	mheader := make(map[string]string)
	for k, v := range r.Headers {
		mheader[k] = v
	}
	if len(m) == len(r.ParamMap) {
		for k, v := range r.ParamMap {
			for _, i := range v {
				switch i {
				case "tbyUrl":
					murl = strings.Replace(r.URL, "${"+k+"}", m[k], -1)
				case "tbyData":
					mdata = strings.Replace(r.Data, "${"+k+"}", m[k], -1)
				default:
					if h, ok := mheader[i]; ok {
						mheader[i] = strings.Replace(h, "${"+k+"}", m[k], -1)

					} else {
						resInfo.URL = murl
						resInfo.Status = false
						resInfo.Msg = "header not has " + i
						return false, resInfo

					}
				}
			}
		}

	} else {
		md, err := json.Marshal(m)
		if err != nil {
			resInfo.URL = murl
			resInfo.Status = false
			resInfo.Msg = "mdata:" + err.Error()
			return false, resInfo

		}
		rd, err := json.Marshal(r.ParamMap)
		if err != nil {
			resInfo.URL = murl
			resInfo.Status = false
			resInfo.Msg = "rpdata:" + err.Error()
			return false, resInfo

		}
		resInfo.URL = murl
		resInfo.Status = false
		resInfo.Msg = "param not match,expect:" + string(md) + ",real:" + string(rd)
		return false, resInfo

	}
	resInfo.URL = murl
	var datareader io.Reader

	switch r.DataForm {
	case "json":
		// jsonStr, err := json.Marshal(r.Data)
		// if err != nil {
		// 	return false, "data:" + err.Error()
		// }
		// r.DataReader = bytes.NewBuffer(jsonStr)
		datareader = bytes.NewBuffer([]byte(mdata))

	case "form":
		datareader = strings.NewReader(mdata)
	case "file":
		bodyBuf := bytes.NewBufferString("")
		bodyWriter := multipart.NewWriter(bodyBuf)

		jsonStr := make(map[string]string)
		err := json.Unmarshal([]byte(mdata), &jsonStr)
		if err != nil {
			resInfo.Status = false
			resInfo.Msg = "data:" + err.Error()
			return false, resInfo

		}

		_, err = bodyWriter.CreateFormFile(jsonStr["tbyfiletype"], jsonStr["tbyfilename"])
		if err != nil {
			resInfo.Status = false
			resInfo.Msg = "data:" + err.Error()
			return false, resInfo
		}
		// fh, err := os.Open(jsonStr["tbyfilename"])
		// if err != nil {
		// 	return false, "data:" + err.Error()
		// }
		for key, val := range jsonStr {
			if key == "tbyfilename" || key == "tbyfiletype" {
				continue
			}
			_ = bodyWriter.WriteField(key, val)
		}
		err = bodyWriter.Close()
		if err != nil {
			resInfo.Status = false
			resInfo.Msg = "data:" + err.Error()
			return false, resInfo
		}
		// boundary := bodyWriter.Boundary()

		// closeBuf := bytes.NewBufferString(fmt.Sprintf("\r\n--%s--\r\n", boundary))
		datareader = io.MultiReader(bodyBuf)
		mheader["Content-Type"] = bodyWriter.FormDataContentType()
		// _, err = fh.Stat()
		// if err != nil {

		// 	return false, "data:" + err.Error()
		// }
	default:
		if strings.ToLower(r.Method) == "post" || strings.ToLower(r.Method) == "put" {
			resInfo.Status = false
			resInfo.Msg = "data:error dataform"
			return false, resInfo

		}

	}

	req, err := http.NewRequest(strings.ToUpper(r.Method), murl, datareader)
	if err != nil {
		resInfo.ReqData = mdata

		resInfo.Status = false
		resInfo.Msg = "newrequest:" + err.Error()
		return false, resInfo

	}
	if r.IsLook {
		resInfo.ReqData = mdata
		resInfo.ReqHeader = mheader
	}

	for k, v := range mheader {
		req.Header.Add(k, v)
	}
	if req != nil {

		resInfo.ReqContentLength = req.ContentLength

	}
	// fmt.Println(req)
	resInfo.IsRun = true
	resInfo.StartTime = time.Now().UnixNano() / 1e5
	resp, err := HTTPClient.Do(req)
	resInfo.EndTime = time.Now().UnixNano() / 1e5
	resInfo.EndTimeSecond = resInfo.EndTime / 1e4
	resInfo.SpeedTime = resInfo.EndTime - resInfo.StartTime

	if resp != nil {
		resInfo.ResStatus = resp.Status
		resInfo.StatusCode = resp.StatusCode
		resInfo.ResHeader = make(map[string]string)
		if r.HasResHeader || r.IsLook {
			for k, v := range resp.Header {
				ss := ""
				for _, i := range v {
					if strings.Index(ss, i) == -1 {
						ss += i + ";"
					}
				}
				if ss == "" {
					resInfo.ResHeader[k] = ""
				} else {
					resInfo.ResHeader[k] = ss[:len(ss)-1]
				}

			}
		}

		resInfo.ResContentLength = resp.ContentLength
		resInfo.ResBody = ""
	}

	if err != nil {
		resInfo.ReqData = mdata
		resInfo.ReqHeader = mheader
		resInfo.Status = false
		resInfo.Msg = "request:" + err.Error()
		return false, resInfo

	}
	// resMap["endTime"] = time.Now().UnixNano() / 1e5

	defer resp.Body.Close()
	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		resInfo.ReqData = mdata
		resInfo.ReqHeader = mheader
		resInfo.Status = false
		resInfo.Msg = "response:" + err.Error()
		return false, resInfo

	}
	if resp.ContentLength == -1 {
		resInfo.ResContentLength = int64(len(content))
	}
	if r.HasResBody || r.IsLook {
		resInfo.ResBody = *(*string)(unsafe.Pointer(&content))
	}

	resInfo.Status = true
	return true, resInfo

}

// func ResDo(r *ResInfo) {

// }
